package web

import (
	"bhttp/interceptor"
	"binterfaces/ihttp"
	"binterfaces/ilogger"
	logger "blogger"
	"embed"
	"errors"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
)

type (
	// QuickWebEngine web引擎
	QuickWebEngine struct {
		log               ilogger.ILogger
		Port              int64                      // 端口
		tree              map[string]ihttp.IRouter   // 路由tree
		pool              sync.Pool                  // 上下文对象池
		interceptors      []ihttp.IRouterInterceptor // 拦截器
		basePath          string                     // 基础路径
		notfoundPath      ihttp.HandlerFunc          // 路由没有找到
		beforeInterceptor ihttp.HandlerFunc          // 前置拦截器拦截处理器
		afterInterceptor  ihttp.HandlerFunc          // 后置拦截器拦截处理器
	}
)

func (engine *QuickWebEngine) port() string {
	return fmt.Sprintf(":%d", engine.Port)
}

// ServeHTTP Handler
func (engine *QuickWebEngine) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	// 初始化上下文
	ctx := engine.getPool()
	ctx.Build(response, request)
	// 匹配路径对应的handler
	_ = engine.matchPath(ctx)
	engine.putPool(ctx)
}

func (engine *QuickWebEngine) getPool() ihttp.IHttpContext {
	ctx := engine.pool.Get()
	if ctx == nil {
		ctx = NewContext(engine.log)
	}
	return ctx.(ihttp.IHttpContext)
}

// before 执行拦截器前置方法
func (engine *QuickWebEngine) before(ctx ihttp.IHttpContext) error {
	// 执行拦截器前置方法
	if len(engine.interceptors) > 0 {
		for _, routerInterceptor := range engine.interceptors {
			if !routerInterceptor.Before(ctx) {
				engine.log.WarnF("请求[%s:%s] => 前置[%s]验证失败", ctx.Method(), ctx.Path(), routerInterceptor.Name())
				return errors.New(routerInterceptor.Name() + "前置Handler验证失败")
			}
		}
	}
	return nil
}

// after 执行拦截器后置方法
func (engine *QuickWebEngine) after(ctx ihttp.IHttpContext) error {
	// 执行拦截器后置方法
	if len(engine.interceptors) > 0 {
		for _, routerInterceptor := range engine.interceptors {
			if !routerInterceptor.After(ctx) {
				engine.log.WarnF("请求[%s:%s] => 后置[%s]验证失败", ctx.Method(), ctx.Path(), routerInterceptor.Name())
				return errors.New(routerInterceptor.Name() + "后置Handler验证失败")
			}
		}
	}
	return nil
}

func (engine *QuickWebEngine) matchPath(ctx ihttp.IHttpContext) error {

	var executeHandler = func() error {
		defer func() {
			if err := recover(); err != nil {
				engine.log.ErrorF("请求[%s:%s] => 执行发生异常：%s", ctx.Method(), ctx.Path(), err)
				ctx.Response().Json(500, err)
			}
		}()
		engine.Match(ctx)(ctx)
		return nil
	}

	// 执行拦截器前置方法
	if err := engine.before(ctx); err != nil {
		engine.beforeInterceptor(ctx)
		return nil
	}
	_ = executeHandler()
	// 执行拦截器后置方法
	if err := engine.after(ctx); err != nil {
		engine.afterInterceptor(ctx)
		return nil
	}
	return nil
}

func (engine *QuickWebEngine) Match(ctx ihttp.IHttpContext) ihttp.HandlerFunc {
	var (
		path             = ctx.Path()
		method           = ctx.Method()
		params           = new(HttpRouterParams)
		pathArray        []string
		findRouter       func(int, []string, *HttpRouterParams, ihttp.IRouter) (ihttp.IRouter, error)
		findStaticRouter func(path string, router ihttp.IRouter) (ihttp.IRouter, error)

		router  ihttp.IRouter
		handler ihttp.HandlerFunc
		exist   bool
		err     error
	)
	// 递归查找路由节点
	findRouter = func(i int, paths []string, params *HttpRouterParams, router ihttp.IRouter) (ihttp.IRouter, error) {
		if router.Path() == paths[i] || strings.HasPrefix(router.Path(), ":") {
			if strings.HasPrefix(router.Path(), ":") {
				params.params = append(params.params, HttpRouterParam{key: router.Path()[1:], value: paths[i]})
			}
			if i+1 >= len(paths) {
				return router, nil
			}
			for _, child := range router.Children() {
				if foundRouter, err := findRouter(i+1, paths, params, child); err == nil {
					return foundRouter, nil
				}
			}
		}
		return nil, errors.New("not found path")
	}
	findStaticRouter = func(path string, router ihttp.IRouter) (ihttp.IRouter, error) {
		return router, nil
	}

	pathArray = strings.Split(path[1:], "/")
	if router, err = findRouter(0, pathArray, params, engine.tree["api"]); err != nil {
		if router, err = findStaticRouter(path, engine.tree["static"]); err != nil {
			engine.log.WarnF("没有匹配的请求路由 [%s:%s]", method, path)
			return engine.notfoundPath
		}
	}
	// 是否存在对应的Handler
	if handler, exist = router.Handler(method); !exist {
		engine.log.WarnF("路由请求方式不支持 [%s:%s]", method, path)
		return engine.notfoundPath
	}
	ctx.Request().SetParam(params)
	return handler
}

func (engine *QuickWebEngine) putPool(ctx ihttp.IHttpContext) {
	engine.pool.Put(ctx)
}

// printUrlPath 输出请求路径
func (engine *QuickWebEngine) printUrlPath() {

	var traversePaths func(router ihttp.IRouter, path []string)

	traversePaths = func(router ihttp.IRouter, path []string) {

		path = append(path, router.Path())

		for _, item := range router.Methods() {
			engine.log.InfoF("[%6s] => [/%s]", strings.ToLower(item), strings.Join(path, "/"))
		}

		if len(router.Children()) == 0 {
			return
		}
		for _, r := range router.Children() {
			traversePaths(r, path)
		}
		// 回溯
		path = path[:len(path)-1]
	}

	for _, v := range engine.tree {
		traversePaths(v, []string{})
	}
}

// Start 启动
func (engine *QuickWebEngine) Start(handlers ...func()) {
	engine.printUrlPath()
	go func() {
		engine.log.InfoF("启动HTTP服务器[%s]", engine.port())
		if err := http.ListenAndServe(engine.port(), engine); err != nil {
			engine.log.FailF("启动服务器失败:%s", err.Error())
			panic(err)
		}
	}()
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	for _, handler := range handlers {
		handler()
	}
	engine.log.Info("服务停止中......")
}

// Routes 构建路由表
func (engine *QuickWebEngine) Routes(basePath ...string) ihttp.IRouter {
	if len(basePath) > 0 {
		engine.basePath = basePath[0]
	} else {
		engine.basePath = ""
	}
	router := NewRouter(engine.basePath)
	engine.tree["api"] = router
	return router
}

func (engine *QuickWebEngine) StaticEmbed(urlPath string, filepath string, embed embed.FS) {
	engine.tree["static"] = NewStaticRouter(urlPath, filepath, embed)
}

// Default 默认
func Default() *QuickWebEngine {
	return &QuickWebEngine{
		log:          logger.DefaultLogger(),
		Port:         8080,
		tree:         make(map[string]ihttp.IRouter),
		interceptors: []ihttp.IRouterInterceptor{interceptor.NewLogger()},
		notfoundPath: func(ctx ihttp.IHttpContext) {
			ctx.Log().ErrorF("资源[%s:%s]没有找到", ctx.Method(), ctx.Path())
		},
	}
}

func NewHttp(log ilogger.ILogger, port int64) *QuickWebEngine {
	return &QuickWebEngine{
		log:          log,
		Port:         port,
		tree:         make(map[string]ihttp.IRouter),
		interceptors: []ihttp.IRouterInterceptor{interceptor.NewLogger()},
		notfoundPath: func(ctx ihttp.IHttpContext) {
			ctx.Log().ErrorF("资源[%s:%s]没有找到", ctx.Method(), ctx.Path())
		},
	}
}
